package com.extsci.webmail.web.service;

import com.extsci.webmail.common.entity.mail.Attachment;
import com.extsci.webmail.common.entity.mail.Mail;
import com.extsci.webmail.common.entity.mail.MailAttachment;
import com.extsci.webmail.common.entity.mail.MailUser;
import com.extsci.webmail.common.util.MyFileUtils;
import com.extsci.webmail.common.util.TimeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * 邮件处理服务
 */
@Service
public class MailHandleService {
    private static final String CHARSET = "UTF-8";
    private static final String CONTENT_TYPE = "text/html;charset=UTF-8";
    private static final String SUBTYPE = "mixed";
    private static final String PRIORITY = "X-Priority";
    private static final String RECEIPT = "Disposition-Notification-To";

    private final AttachmentHandleService attachmentHandleService;

    @Autowired
    public MailHandleService(AttachmentHandleService attachmentHandleService) {
        this.attachmentHandleService = attachmentHandleService;
    }

    /**
     * 创建邮件
     */
    public MimeMessage createMimeMessage(Mail mail, Session session) throws UnsupportedEncodingException, MessagingException {
        MimeMessage mimeMessage = new MimeMessage(session);
        // 发件人
        mimeMessage.setFrom(new InternetAddress(mail.getSenderUser().getAddress(), mail.getSenderUser().getNickName(), CHARSET));
        // 处理收件人列表
        if (mail.getToUsers() != null && mail.getToUsers().size() > 0){
            mimeMessage.setRecipients(Message.RecipientType.TO, mailUserListToAddressArray(mail.getToUsers()));
        }
        // 处理抄送人列表
        if (mail.getCcUsers() != null && mail.getCcUsers().size() > 0){
            mimeMessage.setRecipients(Message.RecipientType.CC, mailUserListToAddressArray(mail.getCcUsers()));
        }
        // 处理密送人列表
        if (mail.getBccUsers() != null && mail.getBccUsers().size() > 0){
            mimeMessage.setRecipients(Message.RecipientType.BCC, mailUserListToAddressArray(mail.getBccUsers()));
        }
        // 邮件主题
        mimeMessage.setSubject(Optional.ofNullable(mail.getSubject()).orElse(""), CHARSET);
        // 邮件内容(body + 附件)
        mimeMessage.setContent(getMessageContentByMail(mail.getBody(), mail.getAttachments()), "text/html;charset=utf-8");
        // 邮件优先度 1：紧急 3:普通
        mimeMessage.addHeader(PRIORITY, Optional.ofNullable(mail.getIsUrgent()).map(isUrgent -> { return isUrgent ? "1" : "3"; }).orElse("3"));
        // 回执
        if (mail.getIsReceipt() != null && mail.getIsReceipt()){
            mimeMessage.addHeader(RECEIPT, "1");
        }
        // 发送时间
        mimeMessage.setSentDate(mail.getSendDate());
        // 保存
        mimeMessage.saveChanges();
        return mimeMessage;
    }

    /**
     * 解析邮件
     * @param flag true:详细信息 false:简单信息
     */
    public Mail parseMimeMessage(MimeMessage mimeMessage, String username, String mailBoxType, long uid, boolean flag) throws MessagingException, IOException {
        Mail mail = new Mail().setUid(uid);
        // 主题
        mail.setSubject(Optional.ofNullable(mimeMessage.getSubject()).map(subject -> {
            try {
                return MimeUtility.decodeText(subject);
            } catch (UnsupportedEncodingException e) {
                return "";
            }
        }).orElse(""));
        // 发件人
        InternetAddress sender = Optional.ofNullable(mimeMessage.getFrom()).map(froms -> { return froms.length > 0 ? (InternetAddress) froms[0] : null; }).orElse(null);
        mail.setSenderUser(new MailUser(sender.getAddress(), sender.getPersonal()));
        // 收件人
        mail.setToUsers(getReceiveAddress(mimeMessage, Message.RecipientType.TO));
        // 抄送人
        mail.setCcUsers(getReceiveAddress(mimeMessage, Message.RecipientType.CC));
        // 密送人
        mail.setBccUsers(getReceiveAddress(mimeMessage, Message.RecipientType.BCC));
        // 发件时间
        mail.setSendTimeStamp(Optional.ofNullable(mimeMessage.getSentDate()).map(d -> TimeUtils.dateToTimestramp(d)).orElse(null));
        // 将收到时间转换为时间戳
        mail.setReceiveTimeStamp(Optional.ofNullable(mimeMessage.getReceivedDate()).map(d -> TimeUtils.dateToTimestramp(d)).orElse(null));
        // 获取是否已读
        mail.setIsSeen(mimeMessage.getFlags().contains(Flags.Flag.SEEN));

        // 获取附件内容
        List<MailAttachment> attachments = new ArrayList<>();
        getAttachments(mimeMessage, attachments, 0);
        mail.setIsHaveAttachment(attachments.size() > 0);
        if (!flag) {
            return mail;
        }
        // ------------------------  详细信息 ---------------------------
        // 获取相对地址 /resource/a@b.c/mailBoxType/uid
        String relativePath = MyFileUtils.contactUri(
                MyFileUtils.FILE_SEPARATOR,
                MyFileUtils.RESOURCE,
                MyFileUtils.FILE_SEPARATOR,
                username,
                MyFileUtils.FILE_SEPARATOR,
                mailBoxType,
                MyFileUtils.FILE_SEPARATOR,
                String.valueOf(uid));

        // 邮件中的图片
        Map<String, String> images = new HashMap<>();
        // 获取邮件正文内容
        mail.setBody(parseBody(images, mimeMessage, MyFileUtils.contactUri(attachmentHandleService.filePath,relativePath), MyFileUtils.contactUri(attachmentHandleService.fileUrl, relativePath)));
        // 附件
        mail.setAttachments(new ArrayList<>());
        // 拼接邮件路径
        for (MailAttachment attachment: attachments) {
            // 将附件保存到本地，返回附件名称
            Attachment fileParam = attachmentHandleService.inputStream2Local(
                    attachment.getDataHandler().getInputStream(),
                    MyFileUtils.contactUri(attachmentHandleService.filePath, relativePath, MyFileUtils.FILE_SEPARATOR, attachment.getCid()),
                    MimeUtility.decodeText(attachment.getDataHandler().getName())
            );
            // 转换为url
            String httpUrl = MyFileUtils.contactUri(
                    attachmentHandleService.fileUrl,
                    relativePath,
                    MyFileUtils.FILE_SEPARATOR,
                    attachment.getCid(),
                    MyFileUtils.FILE_SEPARATOR,
                    MyFileUtils.changeSpecialWord(fileParam.getName())
            );
            mail.getAttachments().add(new Attachment().setName(fileParam.getName()).setUri(httpUrl).setSize(fileParam.getSize()));
        }
        // 获取优先级
        mail.setIsUrgent(isMessageUrgent(mimeMessage));
        // 获取是否需要回执
        mail.setIsReceipt(isMimeMessageReplySign(mimeMessage));
        return mail;
    }


    /**
     * 邮件正文解析
     */
    private String parseBody(Map<String, String> imgs, Part part, String filePath, String urlPath) throws IOException, MessagingException {
        StringBuffer bodyHtmlBuf = new StringBuffer();
        StringBuffer bodyTextBuf = new StringBuffer();
        getMailContent(bodyHtmlBuf,bodyTextBuf, imgs, part, filePath, urlPath);
        String content = null;
        if (bodyHtmlBuf.length() > 0){
            content = bodyHtmlBuf.toString();
            //替换原文中的图片，原始图片标签为<img src="cid:image001.jpg@01D24963.3B4B8280">
            for (String contentId : imgs.keySet()) {
                String replacedText = "cid:" + contentId.replace("<", "").replace(">", "");
                content = content.replace(replacedText, imgs.get(contentId));
            }
        }else {
            content = bodyTextBuf.toString();
        }
        return Optional.ofNullable(content).orElse("");
    }

    /**
     * 解析邮件内容
     */
    public void getMailContent(StringBuffer html,StringBuffer text, Map<String, String> imgs, Part p, String filePath, String urlPath) throws MessagingException, IOException {
        // 检查内容是否含有附件
        if (p.isMimeType("multipart/*")) {
            Multipart mp = (Multipart) p.getContent();
            int count = mp.getCount();
            for (int i = 0; i < count; i++) {
                getMailContent(html, text,imgs, mp.getBodyPart(i), filePath, urlPath);
            }
        } else if (p.isMimeType("text/html")) {
            // 检查内容是否为html
            html.append(p.getContent());
        } else if (p.isMimeType("text/plain")) {
            // 检查内容是否为纯文本
            text.append(p.getContent());
        } else if (p.isMimeType("message/rfc822")) {
            // 检查内容是否含有嵌套消息
            getMailContent(html, text,imgs, (Part) p.getContent(), filePath, urlPath);
        } else if (p.isMimeType("image/*")) {
            // 检查内容是否为内嵌图片
            Object content = p.getContent();
            String[] contentId = p.getHeader("Content-ID");
            if (contentId != null && contentId.length != 0){
                String contentID = contentId[0];
                Attachment attachment = attachmentHandleService.inputStream2Local((InputStream) content, filePath, contentID + ".jpg");
                imgs.put(contentID, MyFileUtils.contactUri(urlPath, MyFileUtils.FILE_SEPARATOR, attachment.getName()));
            }
        }
    }


    /**
     * 将收件人列表转换为地址数组
     */
    private Address[] mailUserListToAddressArray(List<MailUser> mailUserList) throws UnsupportedEncodingException {
        InternetAddress[] internetAddressArray = new InternetAddress[mailUserList.size()];
        for (int i = 0; i < mailUserList.size(); i++) {
            MailUser mailUser = mailUserList.get(i);
            internetAddressArray[i] = new InternetAddress(mailUser.getAddress(), Optional.ofNullable(mailUser.getNickName()).orElse(""), CHARSET);
        }
        return internetAddressArray;
    }

    /**
     * 整合邮件内容(body + 附件)
     */
    private MimeMultipart getMessageContentByMail(String body, List<Attachment> attachments) throws MessagingException, UnsupportedEncodingException {
        MimeMultipart mm = new MimeMultipart();
        // 处理文本部分
        MimeBodyPart text = new MimeBodyPart();
        text.setContent(Optional.ofNullable(body).orElse(""), CONTENT_TYPE);
        mm.addBodyPart(text);
        // 处理附件部分
        if (attachments != null && attachments.size() > 0){
            for (int i = 0; i < attachments.size(); i++){
                MimeBodyPart attachment = new MimeBodyPart();
                DataHandler dataHandler = new DataHandler(new FileDataSource(new File(attachments.get(i).getUri())));
                attachment.setDataHandler(dataHandler);
                attachment.setFileName(MimeUtility.encodeText(attachments.get(i).getName()));
                mm.addBodyPart(attachment);
            }
        }
        // 设置混合关系
        mm.setSubType(SUBTYPE);
        return mm;
    }


    /**
     * 根据收件人类型，获取邮件收件人、抄送和密送地址
     * 如果收件人类型为空，则获得所有的收件人地址
     */
    public List<MailUser> getReceiveAddress(MimeMessage msg, Message.RecipientType type) throws MessagingException {
        Address[] addresses = type == null ? msg.getAllRecipients() : msg.getRecipients(type);
        List<MailUser> mailUsers = new ArrayList<>();
        if (addresses == null) {
            return mailUsers;
        }
        for (Address address : addresses) {
            InternetAddress var1 = (InternetAddress) address;
            mailUsers.add(new MailUser(var1.getAddress(), var1.getPersonal()));
        }
        return mailUsers;
    }

    /**
     * 获取附件
     */
    public void getAttachments(Part part, List<MailAttachment> attachments, Integer index) throws MessagingException, IOException {
        if (part.isMimeType("multipart/mixed")) {
            Multipart multipart = (Multipart) part.getContent();
            //复杂体邮件包含多个邮件体
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                //获得复杂体邮件中其中一个邮件体
                BodyPart bodyPart = multipart.getBodyPart(i);
                //某一个邮件体也有可能是由多个邮件体组成的复杂体
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    if (bodyPart.getDataHandler().getName() != null) {
                        attachments.add(new MailAttachment(String.valueOf(index ++), bodyPart.getDataHandler()));
                    }
                } else if (bodyPart.isMimeType("multipart/*")) {
                    getAttachments(bodyPart, attachments, index);
                } else {
                    if (bodyPart.getDataHandler().getName() != null) {
                        attachments.add(new MailAttachment(String.valueOf(index ++), bodyPart.getDataHandler()));
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            getAttachments((Part) part.getContent(), attachments, index);
        }
    }


    /**
     * 判断邮件是否需要阅读回执
     */
    public boolean isMimeMessageReplySign(MimeMessage msg) throws MessagingException {
        String[] headers = msg.getHeader("Disposition-Notification-To");
        return Optional.ofNullable(headers).map(h -> true).orElse(false);
    }

    /**
     * 判断邮件是否紧急
     */
    public boolean isMessageUrgent(MimeMessage msg) throws MessagingException {
        String[] headers = msg.getHeader(PRIORITY);
        return Optional.ofNullable(headers).map(h -> {
            String headerPriority = h[0];
            return headerPriority.indexOf("1") != -1 || headerPriority.indexOf("High") != -1;
        }).orElse(false);
    }

}
